package model1

import "fmt"

/*
defer语句
Go语言中的defer语句会将其后面跟随的语句进行延迟处理。
在defer归属的函数即将返回时，将延迟处理的语句按defer定义的逆序进行执行，
也就是说，先被defer的语句最后被执行，最后被defer的语句，最先被执行。

举个例子：
*/
func TestDefer() {
	fmt.Println("start")
	defer fmt.Println(1)
	defer fmt.Println(2)
	defer fmt.Println(3)
	fmt.Println("end")
}

/*
执行结果
start
end
3
2
1
由于defer语句延迟调用的特性，所以defer语句能非常方便的处理资源释放问题。比如：资源清理、文件关闭、解锁及记录时间等。
在Go语言的函数中return语句在底层并不是原子操作，它分为给返回值赋值和RET指令两步。
而defer语句执行的时机就在返回值赋值操作后，RET指令执行前。具体如下图所示：
https://www.liwenzhou.com/images/Go/func/defer.png
*/

/*
defer经典案例
阅读下面的代码，写出最后的打印结果。
*/
func f1() int {
	x := 5
	defer func() {
		x++
	}()
	// return 之前给返回值赋值
	// 返回值 返回参数列表写参数名称以参数名字为句柄，没有就有一个虚拟的参数被赋值
	// 赋值后再执行 defer 操作，这时的x和返回值无关了，返回值就是5了
	// 执行完成后方法执行完毕，打印结果就是5了
	return x
}

func f2() (x int) {
	defer func() {
		x++
	}()
	// return 之前给返回值赋值
	// 返回值 返回参数列表写参数名称以参数名字为句柄，没有就有一个虚拟的参数被赋值
	// 赋值后再执行 defer 操作，现在返回参数列表有返回值的句柄，defer方法自增后 那么返回值就+1了
	// 执行完成后方法执行完毕，打印结果就是6了
	return 5
}

func f3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	//执行操作类型f1
	return x
}
func f4() (x int) {
	defer func(x int) {
		x++
	}(x)
	//执行操作类型f2 但是执行匿名函数时是值传递，这时的x就和返回值无关了，结果就是5
	return 5
}
func TestDefer2() {
	fmt.Println(f1()) //5
	fmt.Println(f2()) //6
	fmt.Println(f3()) //5
	fmt.Println(f4()) //5
}

//defer面试题
func calc2(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}
func TestCalc2() {
	y := 1
	x := 2
	defer calc2("AA", x, calc2("A", x, y))
	x = 10
	defer calc2("BB", x, calc2("B", x, y))
	y = 20
	//（提示：defer注册要延迟执行的函数时该函数所有的参数都需要确定其值）
	// 但是会先执行defer 操作中的方法参数列表中的方法 也就是说 先执行 第一个defer 方法参数列表中的方法 打印 A, 2 ,1 ,3 最开始的defer 参数列表就确认了 AA ,2 , 3
	// 然后执行第二个defer 方法参数列表中的方法  这时x =10 打印 B,10 ,1 ,11  参数列表就确认了 BB ,10 , 11
	// 然后真正执行defer最后一个 打印 BB ,10 , 11 , 21
	// 最后执行最开始的defer 打印 AA ,2 , 3 , 5
}
